#pragma once

#include <string>
#include <cstdint>
#include <map>
#include <set>
#include <iostream>
#include <random>
#include "asio.hpp"
#include "chat_message.h"


enum {NAT_1 = 1, NAT_2, NAT_3, NAT_4, NAT_INV = 0xff};

class PeerInfo {
    
public:
    PeerInfo(std::string ip, std::string port, std::string nat)
        : ip_(ip)
        , init_base_port_(std::stoi(port))
        , cur_base_port_(std::stoi(port) + 1)
        , nat_(nat)
        , reported_(false) 
        , punch_done_(false)
        , res_port_(0)
    {
        
        ports_.insert(std::stoi(port));
    }

    bool ok() { return res_port_ != 0; }

    uint16_t res_port() { return res_port_; }
    void res_port(uint16_t port) { res_port_ = port; }

    std::string ip() { return ip_; }

    std::set<uint16_t> ports() { return ports_; }

    bool has_port(std::string port) { return ports_.count(std::stoi(port)) > 0; }
    bool has_port(uint16_t port) { return ports_.count(port) > 0; }

    bool reported() { return reported_; }
    void reported(bool report) { reported_ = report; }

#define num_range(x, min, max) ((x) < (min) ? (min) : ((x) - (min)) % ((max) - (min)) + (min))
    void refresh_port(int num);
    void insert(uint16_t port) { ports_.insert(port); }

    std::string nat() { return nat_; }

    void dump() {
        std::cout << "ip: " << ip_ << std::endl;
        for (auto port : ports_) {
            std::cout << "----port: " << port << std::endl;
        }
    }

private:
    std::string ip_;
    std::set<uint16_t> ports_;
    std::string nat_;

    uint16_t init_base_port_; // 对端反射的port，作为NAT_3-->NAT_4打洞的基准port
    uint16_t cur_base_port_; //每次更新后的基准port
    bool punch_done_;
    uint16_t res_port_; // 最终打通的port
    bool reported_;
};

using PeerInfoPtr = std::shared_ptr<PeerInfo>;

class PeerMgr {
public:
    void insert(const std::string& ip, const std::string& port, const std::string& nat);

    void refresh_peers();

    std::set<PeerInfoPtr> peers() { return peers_; }

    void dump() {
        std::cout << "peer count: " << peers_.size() << std::endl;
        for (const auto& pi : peers_) {
            pi->dump();
        }
    }

    void local_nat_type(uint8_t local_nat_type) {
        local_nat_type_ = local_nat_type;
    }

private:

    std::set<PeerInfoPtr> peers_;
    uint8_t local_nat_type_;
};